<template>
  <div class="app-container">
    <doc-alert title="工作流" url="https://doc.iocoder.cn/bpm" />

    <!-- 搜索工作栏 -->
    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
      <el-form-item label="流程标识" prop="key">
        <el-input v-model="queryParams.key" placeholder="请输入流程标识" clearable style="width: 240px;"
                  @keyup.enter.native="handleQuery"/>
      </el-form-item>
      <el-form-item label="流程名称" prop="name">
        <el-input v-model="queryParams.name" placeholder="请输入流程名称" clearable style="width: 240px;"
                  @keyup.enter.native="handleQuery"/>
      </el-form-item>
      <el-form-item label="流程分类" prop="category">
        <el-select v-model="queryParams.category" placeholder="流程分类" clearable style="width: 240px">
          <el-option v-for="dict in categoryDictDatas" :key="parseInt(dict.value)" :label="dict.label" :value="parseInt(dict.value)"/>
        </el-select>
      </el-form-item>
      <el-form-item>
        <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
        <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
      </el-form-item>
    </el-form>

    <!-- 操作工具栏 -->
    <el-row :gutter="10" class="mb8">
      <el-col :span="1.5">
        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
                   v-hasPermi="['bpm:model:create']">新建流程</el-button>
      </el-col>
      <el-col :span="1.5">
        <el-button type="info" icon="el-icon-upload2" size="mini" @click="handleImport"
                   v-hasPermi="['bpm:model:import']">导入流程</el-button>
      </el-col>
      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
    </el-row>

    <!-- 列表 -->
    <el-table v-loading="loading" :data="list">
      <el-table-column label="流程标识" align="center" prop="key" />
      <el-table-column label="流程名称" align="center" prop="name" width="200">
        <template slot-scope="scope">
          <el-button type="text" @click="handleBpmnDetail(scope.row)">
            <span>{{ scope.row.name }}</span>
          </el-button>
        </template>
      </el-table-column>
      <el-table-column label="流程分类" align="center" prop="category" width="100">
        <template slot-scope="scope">
          <dict-tag :type="DICT_TYPE.BPM_MODEL_CATEGORY" :value="scope.row.category" />
        </template>
      </el-table-column>
      <el-table-column label="表单信息" align="center" prop="formType" width="200">
        <template slot-scope="scope">
          <el-button v-if="scope.row.formId" type="text" @click="handleFormDetail(scope.row)">
            <span>{{ scope.row.formName }}</span>
          </el-button>
          <el-button v-else-if="scope.row.formCustomCreatePath" type="text" @click="handleFormDetail(scope.row)">
            <span>{{ scope.row.formCustomCreatePath }}</span>
          </el-button>
          <label v-else>暂无表单</label>
        </template>
      </el-table-column>
      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
        <template slot-scope="scope">
          <span>{{ parseTime(scope.row.createTime) }}</span>
        </template>
      </el-table-column>
      <el-table-column label="最新部署的流程定义" align="center">
        <el-table-column label="流程版本" align="center" prop="processDefinition.version" width="80">
          <template slot-scope="scope">
            <el-tag size="medium" v-if="scope.row.processDefinition">v{{ scope.row.processDefinition.version }}</el-tag>
            <el-tag size="medium" type="warning" v-else>未部署</el-tag>
          </template>
        </el-table-column>
        <el-table-column label="激活状态" align="center" prop="processDefinition.version" width="80">
          <template slot-scope="scope">
            <el-switch v-if="scope.row.processDefinition" v-model="scope.row.processDefinition.suspensionState"
                       :active-value="1" :inactive-value="2" @change="handleChangeState(scope.row)" />
          </template>
        </el-table-column>
        <el-table-column label="部署时间" align="center" prop="deploymentTime" width="180">
          <template slot-scope="scope">
            <span v-if="scope.row.processDefinition">{{ parseTime(scope.row.processDefinition.deploymentTime) }}</span>
          </template>
        </el-table-column>
      </el-table-column>
      <el-table-column label="操作" align="center" width="450" fixed="right">
        <template slot-scope="scope">
          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
                     v-hasPermi="['bpm:model:update']">修改流程</el-button>
          <el-button size="mini" type="text" icon="el-icon-setting" @click="handleDesign(scope.row)"
                     v-hasPermi="['bpm:model:update']">设计流程</el-button>
          <el-button size="mini" type="text" icon="el-icon-s-custom" @click="handleAssignRule(scope.row)"
                     v-hasPermi="['bpm:task-assign-rule:query']">分配规则</el-button>
          <el-button size="mini" type="text" icon="el-icon-thumb" @click="handleDeploy(scope.row)"
                     v-hasPermi="['bpm:model:deploy']">发布流程</el-button>
          <el-button size="mini" type="text" icon="el-icon-ice-cream-round" @click="handleDefinitionList(scope.row)"
                     v-hasPermi="['bpm:process-definition:query']">流程定义</el-button>
          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
                     v-hasPermi="['bpm:model:delete']">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
    <!-- 分页组件 -->
    <pagination v-show="total>0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
                @pagination="getList"/>

    <!-- 流程表单配置详情 -->
    <el-dialog title="表单详情" :visible.sync="detailOpen" width="50%" append-to-body>
      <parser :key="new Date().getTime()" :form-conf="detailForm" />
    </el-dialog>

    <!-- 流程模型图的预览 -->
    <el-dialog title="流程图" :visible.sync="showBpmnOpen" width="80%" append-to-body>
      <my-process-viewer key="designer" v-model="bpmnXML" v-bind="bpmnControlForm" />
    </el-dialog>

    <!-- 对话框(添加 / 修改) -->
    <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
      <el-form ref="form" :model="form" :rules="rules" label-width="110px">
        <el-form-item label="流程标识" prop="key">
          <el-input v-model="form.key" placeholder="请输入流标标识" style="width: 330px;" :disabled="!!form.id" />
          <el-tooltip v-if="!form.id" class="item" effect="light" content="新建后，流程标识不可修改！" placement="top">
            <i style="padding-left: 5px;" class="el-icon-question" />
          </el-tooltip>
          <el-tooltip v-else class="item" effect="light" content="流程标识不可修改！" placement="top">
            <i style="padding-left: 5px;" class="el-icon-question" />
          </el-tooltip>
        </el-form-item>
        <el-form-item label="流程名称" prop="name">
          <el-input v-model="form.name" placeholder="请输入流程名称" :disabled="!!form.id" clearable />
        </el-form-item>
        <el-form-item v-if="form.id" label="流程分类" prop="category">
          <el-select v-model="form.category" placeholder="请选择流程分类" clearable style="width: 100%">
            <el-option v-for="dict in categoryDictDatas" :key="dict.value" :label="dict.label" :value="dict.value"/>
          </el-select>
        </el-form-item>
        <el-form-item label="流程描述" prop="description">
          <el-input type="textarea" v-model="form.description" clearable />
        </el-form-item>
        <div v-if="form.id">
          <el-form-item label="表单类型" prop="formType">
            <el-radio-group v-model="form.formType">
              <el-radio v-for="dict in modelFormTypeDictDatas" :key="parseInt(dict.value)" :label="parseInt(dict.value)">
                {{dict.label}}
              </el-radio>
            </el-radio-group>
          </el-form-item>
          <el-form-item v-if="form.formType === 10" label="流程表单" prop="formId">
            <el-select v-model="form.formId" clearable style="width: 100%">
              <el-option v-for="form in forms" :key="form.id" :label="form.name" :value="form.id"/>
            </el-select>
          </el-form-item>
          <el-form-item v-if="form.formType === 20" label="表单提交路由" prop="formCustomCreatePath" >
            <el-input v-model="form.formCustomCreatePath" placeholder="请输入表单提交路由" style="width: 330px;" />
            <el-tooltip class="item" effect="light" content="自定义表单的提交路径，使用 Vue 的路由地址，例如说：bpm/oa/leave/create" placement="top">
              <i style="padding-left: 5px;" class="el-icon-question" />
            </el-tooltip>
          </el-form-item>
          <el-form-item v-if="form.formType === 20" label="表单查看路由" prop="formCustomViewPath">
            <el-input v-model="form.formCustomViewPath" placeholder="请输入表单查看路由" style="width: 330px;" />
            <el-tooltip class="item" effect="light" content="自定义表单的查看路径，使用 Vue 的路由地址，例如说：bpm/oa/leave/view" placement="top">
              <i style="padding-left: 5px;" class="el-icon-question" />
            </el-tooltip>
          </el-form-item>
        </div>
      </el-form>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitForm">确 定</el-button>
        <el-button @click="cancel">取 消</el-button>
      </div>
    </el-dialog>

    <!-- 用户导入对话框 -->
    <el-dialog title="导入流程" :visible.sync="upload.open" width="400px" append-to-body>
      <el-upload ref="upload" :limit="1" accept=".bpmn, .xml" :headers="upload.headers" :action="upload.url"
        :disabled="upload.isUploading" :on-progress="handleFileUploadProgress" :on-success="handleFileSuccess"
        :auto-upload="false" name="bpmnFile" :data="upload.form" drag>
        <i class="el-icon-upload"></i>
        <div class="el-upload__text">
          将文件拖到此处，或
          <em>点击上传</em>
        </div>
        <div class="el-upload__tip" style="color:red" slot="tip">提示：仅允许导入“bpm”或“xml”格式文件！</div>
        <div class="el-upload__tip" slot="tip">
          <el-form ref="uploadForm" size="mini" label-width="90px" :model="upload.form" :rules="upload.rules" @submit.native.prevent>
            <el-form-item label="流程标识" prop="key">
              <el-input v-model="upload.form.key" placeholder="请输入流标标识" style="width: 250px;" />
              <el-tooltip class="item" effect="light" content="新建后，流程标识不可修改！" placement="top">
                <i style="padding-left: 5px;" class="el-icon-question" />
              </el-tooltip>
            </el-form-item>
            <el-form-item label="流程名称" prop="name">
              <el-input v-model="upload.form.name" placeholder="请输入流程名称" clearable />
            </el-form-item>
            <el-form-item label="流程描述" prop="description">
              <el-input type="textarea" v-model="upload.form.description" clearable />
            </el-form-item>
          </el-form>
        </div>
      </el-upload>
      <div slot="footer" class="dialog-footer">
        <el-button type="primary" @click="submitFileForm">确 定</el-button>
        <el-button @click="uploadClose">取 消</el-button>
      </div>
    </el-dialog>

    <!-- ========== 流程任务分配规则 ========== -->
    <taskAssignRuleDialog ref="taskAssignRuleDialog" />
  </div>
</template>

<script>
import {
  deleteModel,
  deployModel,
  getModelPage,
  getModel,
  updateModelState,
  createModel,
  updateModel
} from "@/api/bpm/model";
import {DICT_TYPE, getDictDatas} from "@/utils/dict";
import {getForm, getSimpleForms} from "@/api/bpm/form";
import {decodeFields} from "@/utils/formGenerator";
import Parser from '@/components/parser/Parser'
import {getBaseHeader} from "@/utils/request";
import taskAssignRuleDialog from "../taskAssignRule/taskAssignRuleDialog";

import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";

export default {
  name: "model",
  components: {
    Parser,
    Treeselect,
    taskAssignRuleDialog
  },
  data() {
    return {
      // 遮罩层
      loading: true,
      // 显示搜索条件
      showSearch: true,
      // 总条数
      total: 0,
      // 表格数据
      list: [],
      // 查询参数
      queryParams: {
        pageNo: 1,
        pageSize: 10
      },

      // BPMN 数据
      showBpmnOpen: false,
      bpmnXML: null,
      bpmnControlForm: {
        prefix: "flowable"
      },

      // 流程表单详情
      detailOpen: false,
      detailForm: {
        fields: []
      },

      // 流程表单
      title: "",
      open: false,
      form: {},
      // 表单校验
      rules: {
        key: [{ required: true, message: "流程标识不能为空", trigger: "blur" }],
        name: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
        formType: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
        formId: [{ required: true, message: "业务表单不能为空", trigger: "blur" }],
        category: [{ required: true, message: "流程分类不能为空", trigger: "blur" }],
        formCustomCreatePath: [{ required: true, message: "表单提交路由不能为空", trigger: "blur" }],
        formCustomViewPath: [{ required: true, message: "表单查看路由不能为空", trigger: "blur" }],
      },

      // 流程导入参数
      upload: {
        // 是否显示弹出层（用户导入）
        open: false,
        // 是否禁用上传
        isUploading: false,
        // 设置上传的请求头部
        headers: getBaseHeader(),
        // 上传的地址
        url: process.env.VUE_APP_BASE_API + '/admin-api' + "/bpm/model/import",
        // 表单
        form: {},
        // 校验规则
        rules: {
          key: [{ required: true, message: "流程标识不能为空", trigger: "blur" }],
          name: [{ required: true, message: "流程名称不能为空", trigger: "blur" }],
        },
      },
      // 流程表单的下拉框的数据
      forms: [],

      // 数据字典
      categoryDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_CATEGORY),
      modelFormTypeDictDatas: getDictDatas(DICT_TYPE.BPM_MODEL_FORM_TYPE),
      taskAssignRuleDictDatas: getDictDatas(DICT_TYPE.BPM_TASK_ASSIGN_RULE_TYPE),
    };
  },
  created() {
    this.getList();
    // 获得流程表单的下拉框的数据
    getSimpleForms().then(response => {
      this.forms = response.data
    })
  },
  methods: {
    /** 查询流程模型列表 */
    getList() {
      this.loading = true;
      getModelPage(this.queryParams).then(response => {
          this.list = response.data.list;
          this.total = response.data.total;
          this.loading = false;
        }
      );
    },
    /** 取消按钮 */
    cancel() {
      this.open = false;
      this.reset();
    },
    // 表单重置
    reset() {
      this.form = {
        id: undefined,
        key: undefined,
        name: undefined,
        description: undefined,
        category: undefined,
        formType: undefined,
        formId: undefined,
        formCustomCreatePath: undefined,
        formCustomViewPath: undefined
      };
      this.resetForm("form");
    },
    /** 搜索按钮操作 */
    handleQuery() {
      this.queryParams.pageNo = 1;
      this.getList();
    },
    /** 重置按钮操作 */
    resetQuery() {
      this.dateRange = [];
      this.resetForm("queryForm");
      this.handleQuery();
    },
    /** 新增按钮操作 */
    handleAdd() {
      this.reset();
      this.title = "新建模型";
      this.open = true;
    },
    /** 修改按钮操作 */
    handleUpdate(row) {
      this.reset();
      this.title = "修改模型";
      this.open = true;
      // 设置 form
      this.form = {
        ...row
      };
      // 触发一次校验
      // this.$refs["form"].validate();
    },
    /** 设计按钮操作 */
    handleDesign(row) {
      this.$router.push({
        path:"/bpm/manager/model/design",
        query:{
          modelId: row.id
        }
      });
    },
    /** 提交按钮 */
    submitForm() {
      this.$refs["form"].validate(valid => {
        if (!valid) {
          return;
        }
        // 更新
        if (this.form.id) {
          updateModel({
            ...this.form,
            formId: this.form.formType === 10 ? this.form.formId : undefined,
            formCustomCreatePath: this.form.formType === 20 ? this.form.formCustomCreatePath : undefined,
            formCustomViewPath: this.form.formType === 20 ? this.form.formCustomViewPath : undefined,
          }).then(response => {
            this.$modal.msgSuccess("修改模型成功");
            this.open = false;
            this.getList();
          });
          return;
        }
        // 创建
        createModel(this.form).then(response => {
          this.open = false;
          this.getList();
          this.$alert('<strong>新建模型成功！</strong>后续需要执行如下 4 个步骤：' +
            '<div>1. 点击【修改流程】按钮，配置流程的分类、表单信息</div>' +
            '<div>2. 点击【设计流程】按钮，绘制流程图</div>' +
            '<div>3. 点击【分配规则】按钮，设置每个用户任务的审批人</div>' +
            '<div>4. 点击【发布流程】按钮，完成流程的最终发布</div>' +
            '另外，每次流程修改后，都需要点击【发布流程】按钮，才能正式生效！！！',
            '重要提示', {
              dangerouslyUseHTMLString: true,
              type: 'success'
            });
        });
      });
    },
    /** 删除按钮操作 */
    handleDelete(row) {
      const that = this;
      this.$modal.confirm('是否删除该流程！！').then(function() {
        deleteModel(row.id).then(response => {
          that.getList();
          that.$modal.msgSuccess("删除成功");
        })
      }).catch(() => {});
    },
    /** 部署按钮操作 */
    handleDeploy(row) {
      const that = this;
      this.$modal.confirm('是否部署该流程！！').then(function() {
        deployModel(row.id).then(response => {
          that.getList();
          that.$modal.msgSuccess("部署成功");
        })
      }).catch(() => {});
    },
    /** 流程表单的详情按钮操作 */
    handleFormDetail(row) {
      // 流程表单
      if (row.formId) {
        getForm(row.formId).then(response => {
          // 设置值
          const data = response.data
          this.detailForm = {
            ...JSON.parse(data.conf),
            fields: decodeFields(data.fields)
          }
          // 弹窗打开
          this.detailOpen = true
        })
        // 业务表单
      } else if (row.formCustomCreatePath) {
        this.$router.push({ path: row.formCustomCreatePath});
      }
    },
    /** 流程图的详情按钮操作 */
    handleBpmnDetail(row) {
      getModel(row.id).then(response => {
        this.bpmnXML = response.data.bpmnXml
        // 弹窗打开
        this.showBpmnOpen = true
      })
    },
    /** 跳转流程定义的列表 */
    handleDefinitionList(row) {
      this.$router.push({
        path:"/bpm/manager/definition",
        query:{
          key: row.key
        }
      });
    },
    /** 更新状态操作 */
    handleChangeState(row) {
      const id = row.id;
      let state = row.processDefinition.suspensionState;
      let statusState = state === 1 ? '激活' : '挂起';
      this.$modal.confirm('是否确认' + statusState + '流程名字为"' + row.name + '"的数据项?').then(function() {
        return updateModelState(id, state);
      }).then(() => {
        this.getList();
        this.$modal.msgSuccess(statusState + "成功");
      }).catch(() => {});
    },
    /** 导入按钮操作 */
    handleImport() {
      this.upload.open = true;
    },
    // 文件上传中处理
    handleFileUploadProgress(event, file, fileList) {
      this.upload.isUploading = true;
    },
    // 文件上传成功处理
    handleFileSuccess(response, file, fileList) {
      if (response.code !== 0) {
        this.$modal.msgError(response.msg)
        return;
      }
      // 重置表单
      this.uploadClose();
      // 提示，并刷新
      this.$modal.msgSuccess("导入流程成功！请点击【设计流程】按钮，进行编辑保存后，才可以进行【发布流程】");
      this.getList();
    },
    uploadClose() {
      // 关闭弹窗
      this.upload.open = false;
      // 重置上传状态和文件
      this.upload.isUploading = false;
      this.$refs.upload.clearFiles();
      // 重置表单
      this.upload.form = {};
      this.resetForm("uploadForm");
    },
    /** 提交上传文件 */
    submitFileForm() {
      this.$refs["uploadForm"].validate(valid => {
        if (!valid) {
          return;
        }
        this.$refs.upload.submit();
      })
    },
    /** 处理任务分配规则列表的按钮操作 */
    handleAssignRule(row) {
      this.$refs['taskAssignRuleDialog'].initModel(row.id);
    },
  }
};
</script>

<style lang="scss">
.my-process-designer {
  height: calc(100vh - 200px);
}
</style>
